package com.neural.infrastructure;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import com.neural.descriptor.FeedDescriptor;
import com.neural.descriptor.InputDescriptor;
import com.neural.descriptor.InputSetDescriptor;
import com.neural.descriptor.KohonenState;
import com.neural.descriptor.LayerDescriptor;
import com.neural.descriptor.NetDescriptor;
import com.neural.descriptor.ResultPojo;
import com.neural.descriptor.SizeDescriptor;

public class Net {

	private final List<ILayer> layers;

	public Net(NetDescriptor netDescriptor) {
		this.layers = new ArrayList<ILayer>(netDescriptor.getLayersDescriptor().size());
		ILayer previousLayer = null;
		for (LayerDescriptor layerDescriptor : netDescriptor.getLayersDescriptor()) {
			if ("kohonen".equals(layerDescriptor.getFeature())) {
				previousLayer = new KohonenLayer(layerDescriptor, previousLayer);
			} else if ("grossberg".equals(layerDescriptor.getFeature())) {
				previousLayer = new GrossbergLayer(layerDescriptor, previousLayer);
			} else {
				previousLayer = new NormalLayer(layerDescriptor, previousLayer);
			}
			this.layers.add(previousLayer);
		}
	}

	public List<ResultPojo> feedAndComputeNet(FeedDescriptor feedDescriptor) {

		List<ResultPojo> resultPojos = new LinkedList<ResultPojo>();
		for (InputSetDescriptor inputSetDescriptor : feedDescriptor.getInputSetDescriptors()) {

			for (int i = 0; i < inputSetDescriptor.getRepeats(); i++) {
				for (InputDescriptor pattern : inputSetDescriptor.getSets()) {

					NormalLayer inputLayer = this.getInputLayer();
					inputLayer.setNeuronValues(pattern.getInputs());
					this.grossbergPropagateValues(inputSetDescriptor.getLearningSpeeds(), inputSetDescriptor.getConscience(), inputSetDescriptor.getNeighbourhood(), pattern.getExpectedResults());
					resultPojos.add(new ResultPojo(pattern.getInputs(), this.getOutputValues()));
				}
			}
		}
		return resultPojos;
	}

	public List<KohonenState> returnKohonenNeuronsState() {

		List<KohonenState> kohonenStates = new ArrayList<KohonenState>();
		SizeDescriptor size = getInputLayer().getSizeDescriptor();
		for (Neuron kNeuron : getOutputLayer().getNeurons()) {
			List<List<Double>> matrix = new ArrayList<>();
			for (int i = 0; i < size.getSizeX(); i++) {
				List<Double> elems = new ArrayList<>();
				for (int j = 0; j < size.getSizeY(); j++) {
					elems.add(kNeuron.getPreConnections().get(i * size.getSizeX() + j).getWeight());
				}
				matrix.add(elems);
			}
			kohonenStates.add(new KohonenState(matrix));
		}
		return kohonenStates;
	}

	public NormalLayer getInputLayer() {
		return (NormalLayer) layers.get(0);
	}

	private ILayer getOutputLayer() {
		return layers.get(layers.size() - 1);
	}

	private void grossbergPropagateValues(List<Double> learningSpeeds, Double conscience, Double neighbourhood, List<Double> expectedResults) {

		for (int i = 0; i < layers.size(); i++) {
			layers.get(i).compute(learningSpeeds.get(i), conscience, neighbourhood, expectedResults);
		}
	}

	private List<Double> getOutputValues() {
		return getOutputLayer().getValues();
	}
}